{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "26a08fe8",
   "metadata": {},
   "source": [
    "# The `Utils` notebook  <a id=\"Utils\"></a>  [<font size=1>(back to `Main.ipynb`)</font>](./Main.ipynb)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "561e5f81",
   "metadata": {},
   "source": [
    "This notebook implements a number of useful (though not essential) functions.\n",
    "\n",
    "## The notebook is organized as follows:\n",
    "\n",
    "* a [norm](#norm) function;\n",
    "* a [linear interpolation](#linear-interp) function;\n",
    "* a [function zeroing](#trunc-mat) all values below a threshold;\n",
    "* a [Gini](#Gini) function;\n",
    "* functions for [pretty printing](#pretty-print);\n",
    "* some functions related to [agents remaining in the same state](#same-state-funs) for a number of periods\n",
    "\n",
    "It also contains a number of [deprecated functions](#deprecated), kept for legacy reasons."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2083643d",
   "metadata": {},
   "source": [
    "# The norm function  <a id=\"norm\"></a>[<font size=1>(back to menu)</font>](#Utils)\n",
    "\n",
    "We construct a simple norm function, whose signature is:\n",
    "\n",
    "> `norm(x::AbstractArray; p::Union{Nothing,Int}=nothing)`.\n",
    "\n",
    "For the sake of completeness, we extend it to number using the absolute value.\n",
    "\n",
    "It returns the supremum norm if `p=nothing` and the p-norm otherwise."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "bbbda143",
   "metadata": {},
   "outputs": [],
   "source": [
    "function norm(x::AbstractArray; p::Union{Nothing,Int}=nothing) \n",
    "    return isnothing(p) ? maximum(abs.(x)) : (sum(abs.(x).^p))^(1/p)\n",
    "end;\n",
    "function norm(x::Number; p::Union{Nothing,Int}=nothing) \n",
    "    return abs(x)\n",
    "end;"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "b8a1a7e2",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "10"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "norm([-1,2,-10])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "8aa3ece2",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "10"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "norm([-1,2,-10])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d3ec34ff",
   "metadata": {},
   "source": [
    "# The linear interpolation function  <a id=\"linear-interp\"></a>[<font size=1>(back to menu)</font>](#Utils)\n",
    "\n",
    "\n",
    "The function `interpLinear` is a linear interpolation function, that is more specialized and hence slightly faster than the interpolation function of the `LinearInterpolation` package.\n",
    "\n",
    "The function specification is: \n",
    "> `interpLinear(x::T,xs::Vector{T},ys::Vector{T},n::I,lb_y::T)`, \n",
    "\n",
    "where:\n",
    "* `x` is a scalar where the interpolation is computed;\n",
    "* `xs` and `ys` are sorted vectors such that `(xs[i], ys[i])` for `i=1,...,n` is a set of `n` known points;\n",
    "* `n` is the common length of vectors `xs` and `ys`;\n",
    "* `lb` is the lower bound for the truncation of the interpolation. If the interpolation result is lower than lb, the interpolation is trauncated to lb.\n",
    "\n",
    "The function returns a pair containing:\n",
    "* a scalar corresponding to the interpolated value (truncated if needed);\n",
    "* an index `np` which is such that the interpolation is performed between indices `np` and `np+1`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "61642f5c",
   "metadata": {},
   "outputs": [],
   "source": [
    "function interpLinear(x::T, \n",
    "                      xs::Vector{T},\n",
    "                      ys::Vector{T}, \n",
    "                      n::I, lb_y::T)::Tuple{T,I} where{T<:Real,I<:Integer}\n",
    "   \n",
    "    nx = searchsortedlast(xs, x) \n",
    "    #nx is the largest index such that xs[nx]≤x (and hence xs[nx+1]>x). Returns 0 if x≤xs[1]. xs sorted.\n",
    "        \n",
    "    #Adjust nx if x falls out of bounds of xs\n",
    "    if nx == 0\n",
    "        nx += 1\n",
    "    elseif (nx==n)\n",
    "        nx -= 1\n",
    "    end    \n",
    "\n",
    "    # Linear interpolation in x between xs[nx] and xs[nx+1]\n",
    "    x_l,x_h = xs[nx],xs[nx+1]\n",
    "    y_l,y_h = ys[nx],ys[nx+1]\n",
    "    y = y_l + (y_h-y_l)/(x_h-x_l)*(x-x_l) \n",
    "    \n",
    "    # returns interpolated value and corresponding index\n",
    "    return ((y<lb_y) ? lb_y : y), nx\n",
    "    \n",
    "end"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f84a0e43",
   "metadata": {},
   "source": [
    "# Truncating stochastic matrix  <a id=\"trunc-mat\"></a>[<font size=1>(back to menu)</font>](#Utils)\n",
    "\n",
    "\n",
    "The function `truncMatrix` is function, that sets to zero all values of a matrix below a given threshold. \n",
    "The lost mass is attributed to the diagonal. The function only works for *square* matrices, and raises an error otherwise. \n",
    "\n",
    "The function specification is: \n",
    "> `truncMatrix(M::AbstractMatrix{T},threshold::T=1e-5)::AbstractMatrix{T} where {T<:Real}`, \n",
    "\n",
    "where:\n",
    "* `M` is an abstract matrix;\n",
    "* `threshold` is the threshold below which values are set to 0.\n",
    "\n",
    "The function returns a matrix of the same size as the input."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "id": "11d2e511",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "truncMatrix (generic function with 2 methods)"
      ]
     },
     "execution_count": 68,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "function dimStoch(M::AbstractMatrix{T})::Integer  where {T<:Real}\n",
    "    n,p = size(M)\n",
    "    if all(sum(M,dims=2) .≈ ones(eltype(M),size(sum(M,dims=2))))\n",
    "        return 2\n",
    "    elseif all(sum(M,dims=1) .≈ ones(eltype(M),size(sum(M,dims=1))))\n",
    "        return 1\n",
    "    else\n",
    "        return 0\n",
    "    end\n",
    "end\n",
    "\n",
    "function truncMatrix(M::AbstractMatrix{T},threshold::T)::AbstractMatrix{T} where {T<:Real}\n",
    "    M_ = copy(M)\n",
    "    M_[M_.<=threshold].=zero(T)\n",
    "    (n,p) = size(M)\n",
    "    dim_stoch = dimStoch(M)\n",
    "    @assert (n==p)&&(dim_stoch>0)\n",
    "    for i = 1:n\n",
    "        M_[i,i] += one(T) - sum(M_,dims=dim_stoch)[i]\n",
    "    end\n",
    "    return M_\n",
    "end\n",
    "\n",
    "function truncMatrix(M::AbstractMatrix{T};threshold::T=1e-5)::AbstractMatrix{T} where {T<:Real}\n",
    "    return truncMatrix(M,threshold)\n",
    "end;"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "id": "fc077289",
   "metadata": {},
   "outputs": [
    {
     "ename": "LoadError",
     "evalue": "AssertionError: n == p && dim_stoch > 0",
     "output_type": "error",
     "traceback": [
      "AssertionError: n == p && dim_stoch > 0",
      "",
      "Stacktrace:",
      " [1] truncMatrix(M::Matrix{Float64}, threshold::Float64)",
      "   @ Main ./In[68]:17",
      " [2] truncMatrix(M::Matrix{Float64})",
      "   @ Main ./In[68]:13",
      " [3] top-level scope",
      "   @ In[72]:2"
     ]
    }
   ],
   "source": [
    "M = rand(5,5)\n",
    "#truncMatrix(M)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "890c68c0",
   "metadata": {},
   "source": [
    "# Gini coefficient  <a id=\"Gini\"></a>[<font size=1>(back to menu)</font>](#Utils)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4d576a77",
   "metadata": {},
   "source": [
    "We consider a discrete distribution of variables $\\{y_i\\}_{i=1,\\ldots,n}$ with probabilities $\\{p_{y_i}\\}_{i=1,\\ldots,n}$. The variables $\\{y_i\\}$ are positive and sorted in increasing order ($0<y_i<y_{i+1}$). \n",
    "The Gini coefficient $G$ is then defined as:\n",
    "$$G = 1- \\frac{\\sum_{i=1}^n p_{y_i}(S_{i-1}+S_{i})}{S_n},$$\n",
    "where $S_0=0$ and:\n",
    "$$S_i = \\sum_{j=1}^i y_jp_{y_j}.$$\n",
    "\n",
    "The function signature is:\n",
    "> `Gini(ys::Vector{T}, pys::Vector{T})::T`\n",
    "\n",
    "or \n",
    "\n",
    "> `Gini(ys::Matrix{T}, pys::Matrix{T})::T`\n",
    "\n",
    "where: \n",
    "* `ys` corresponds to $\\{y_i\\}_{i=1,\\ldots,n}$ and is not required to be sorted, \n",
    "* `pys` to $\\{p_{y_i}\\}_{i=1,\\ldots,n}$. \n",
    "\n",
    "The inputs `ys` and `pys` can be either of type `Vector` or `Matrix` (but both of the same type)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "a9f8abdd",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Gini (generic function with 2 methods)"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "function Gini(ys::Vector{T}, pys::Vector{T})::T where{T<:Real}\n",
    "    @assert size(ys)==size(pys)\n",
    "    iys = sortperm(ys)\n",
    "    ys .= ys[iys]\n",
    "    pys .= pys[iys]\n",
    "    Ss = [zero(T); cumsum(ys.*pys)]\n",
    "    return one(T) - sum(pys.*(Ss[1:end-1].+Ss[2:end]))/Ss[end]\n",
    "end\n",
    "function Gini(ys::Matrix{T}, pys::Matrix{T})::T where{T<:Real}\n",
    "    @assert size(ys)==size(pys)\n",
    "    return Gini(ys[:],pys[:])\n",
    "end"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "984f4b10",
   "metadata": {},
   "source": [
    "# Pretty printing <a id=\"pretty-print\"></a>[<font size=1>(back to menu)</font>](#Utils)\n",
    "\n",
    "For [dictionaries](#print-dict) and [arrays](#print-array).\n",
    "\n",
    "## Printing dictionaries <a id=\"print-dict\"></a>[<font size=1>(back to pretty printing)</font>](#pretty-print)\n",
    "\n",
    "The function \n",
    "> `print_dict(dict::Dict{String,T}; sep=':',digits::Int64=4)::Nothing`\n",
    "\n",
    "prints the description (label$\\,\\times\\,$value) in the alphabetical order of the labels, with `digits` digits for the rounding and where label and value are separeted by `sep`. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4cbd4500",
   "metadata": {},
   "outputs": [],
   "source": [
    "function print_dict(dict::Dict{String,T}; sep=\"\",digits=4)::Nothing where {T<:Real}\n",
    "    tuples = sort([(k,v) for (k,v) in dict], by = first)\n",
    "    max_label_length = maximum(map(length∘first,tuples))\n",
    "    for (k,v) in tuples\n",
    "        k_ = lowercase(k)\n",
    "        trail_space = repeat(' ',1+max_label_length-length(k))\n",
    "        to_pct = (occursin(\"to-gdp\",k_)||occursin(\"share\",k_)) \n",
    "        if to_pct\n",
    "            println(k,sep*trail_space,round(100*v,digits=digits-2),\"%\")\n",
    "        else\n",
    "            println(k,sep*trail_space,round(v,digits=digits))\n",
    "        end\n",
    "    end\n",
    "    return nothing\n",
    "end;"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e88ae52a",
   "metadata": {},
   "source": [
    "## Printing arrays <a id=\"print-array\"></a>[<font size=1>(back to pretty printing)</font>](#pretty-print)\n",
    "\n",
    "The function \n",
    "> `print_array(M::AbstractMatrix{T};digits::Int64=4)::Nothing where {T<:Real}`\n",
    "\n",
    "prints the matrix `M` in a proper way (rows and then columns). Decimals are aligned on the decimal separator."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "id": "ceb8de7b",
   "metadata": {},
   "outputs": [],
   "source": [
    "function print_array(M::AbstractMatrix{T};sep=\" \",digits::Int64=4)::Nothing where {T<:Real}\n",
    "    Ms = map(x -> string(round(x,digits=digits)), M)\n",
    "    function length_dec(cs)\n",
    "        css = split(cs,\".\")\n",
    "        ncss = length(css)\n",
    "        if ncss == 1\n",
    "            return 0\n",
    "        elseif ncss == 2\n",
    "            return length(css[2])\n",
    "        else\n",
    "            @error(\"problem in splitting the decimal part of $cs\")\n",
    "        end\n",
    "    end\n",
    "    function length_int(cs)\n",
    "        return length(split(cs,\".\")[1])\n",
    "    end\n",
    "    n_dec_max = maximum(length_dec.(Ms))\n",
    "    n_int_max = maximum(length_int.(Ms))\n",
    "    for i in eachindex(M[:,1])\n",
    "        s = \"\"\n",
    "        for j in eachindex(M[1,:])\n",
    "            s *= repeat(\" \",n_int_max-length_int(Ms[i,j]))*Ms[i,j]*repeat(\" \",n_dec_max-length_dec(Ms[i,j]))*sep\n",
    "        end\n",
    "        println(s[1:end-length(sep)])\n",
    "    end\n",
    "    return nothing\n",
    "end;\n",
    "\n",
    "function print_array(V::AbstractVector{T};sep=\" \",digits::Int64=4)::Nothing where {T<:Real}\n",
    "    return print_array(reshape(V,1,length(V)),sep=sep,digits=digits)\n",
    "end"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "45ad4ecb",
   "metadata": {},
   "source": [
    "# Same-state functions  <a id=\"same-state-funs\"></a>[<font size=1>(back to menu)</font>](#Utils)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bafa41ca",
   "metadata": {},
   "source": [
    "The function \n",
    "> `restricted_trans(iy::I,solution::AiyagariSolution) where I<:Int`\n",
    "\n",
    "returns the transition matrix and the stationary distribution corresponding to `solution` but restricted to idiosyncratic state `iy`.\n",
    "\n",
    "In other words, multiplying $n$ times the obtained transition matrix  to the obtained stationary distribution allows one to obtain the stationary distribution of agents that have been at least for $n$ consecutive periods in state $n$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b1c71bcd",
   "metadata": {},
   "outputs": [],
   "source": [
    "function restricted_trans(iy::I, solution::AiyagariSolution) where I<:Int\n",
    "    transM = copy(solution.transitMat)\n",
    "    statD  = copy(solution.stationaryDist)\n",
    "    (na,ny)= size(statD)\n",
    "    iy′    = iy\n",
    "    for iy in 1:ny\n",
    "        (iy==iy′) && continue\n",
    "        for ia in 1:na\n",
    "            statD[ia,iy] = zero(eltype(statD))\n",
    "        end\n",
    "    end\n",
    "    for iy in 1:ny, jy in 1:ny\n",
    "        (iy==iy′) && (jy==iy′) && continue\n",
    "        for ia in 1:na, ja in 1:na\n",
    "            transM[(iy-1)*na+ia,(jy-1)*na+ja] = zero(eltype(statD))\n",
    "        end\n",
    "    end\n",
    "    return transM, statD\n",
    "end"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bc553b92",
   "metadata": {},
   "source": [
    "The function \n",
    "> `long_term_value(iy::I,solution::AiyagariSolution; policy::Symbol=:ga, tol=1e-6, maxiter=100) where I<:Int`\n",
    "\n",
    "returns the value of the variable `policy` (by default asset, `:ga`) for agents remaining in state `iy` for a long period.\n",
    "\n",
    "For instance, `long_term_value(iy,solution, policy=:gc)` returns the consumption level of agents remaining for a long time in state `iy`. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e7bd600e",
   "metadata": {},
   "outputs": [],
   "source": [
    "function long_term_value(iy::I, solution::AiyagariSolution; symbol::Symbol=:ga, tol=1e-6, maxiter=100) where I<:Int\n",
    "    gx = getproperty(solution,symbol)[:]\n",
    "    transM_iy,statD_iy = restricted_trans(iy,solution);\n",
    "    transM_iy_n = transM_iy^2500\n",
    "    statD_iy_n  = transM_iy_n * statD[:]\n",
    "    toR  = sum(gx.*statD_iy_n)/sum(statD_iy_n)\n",
    "    toR_ = toR\n",
    "    diff = one(eltype(gx))\n",
    "    iter  = 0\n",
    "    while (diff > tol)&&(iter<maxiter)        \n",
    "        statD_iy_n = transM_iy_n * statD_iy_n\n",
    "        toR_ = toR\n",
    "        toR  = sum(gx.*statD_iy_n)/sum(statD_iy_n)\n",
    "        diff = abs(toR-toR_)\n",
    "        iter+= 1\n",
    "    end\n",
    "    return toR\n",
    "end"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1126d159",
   "metadata": {},
   "outputs": [],
   "source": [
    "function long_term_path(iy::I, n::I, x0::T, solution::AiyagariSolution, economy::Economy;\n",
    "         symbol::Symbol=:ga, tol=1e-6, maxiter=100) where {I<:Int,T<:Real}\n",
    "    @unpack aGrid, na, a_min = economy\n",
    "    MS = solution.stationaryDist\n",
    "    gx = getproperty(solution,symbol)\n",
    "    gx_foo(x) = interpLinear(x,aGrid,gx[:,iy],na,a_min)[1]\n",
    "    \n",
    "    toR = zeros(T,n)\n",
    "\n",
    "    toR[1] = x0\n",
    "    for i in eachindex(toR[2:end])\n",
    "        toR[i+1] = gx_foo(toR[i])\n",
    "    end\n",
    "    return toR\n",
    "end"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9593afa2",
   "metadata": {},
   "source": [
    "# Deprecated functions  <a id=\"deprecated\"></a>[<font size=1>(back to menu)</font>](#Utils)\n",
    "\n",
    "These functions are not used anymore but kept for legacy reasons.\n",
    "\n",
    "## Converting from base `E` to base 10 (and vice-versa)\n",
    "\n",
    "\n",
    "### From base `E` to base 10\n",
    "\n",
    "The function `convertBasisE10(vE, E, N)` converts a number expressed in base-`E` (and represented as the vector `vE`) into its decimal representation `p` (as an integer).\n",
    "\n",
    "***Remarks*** \n",
    "* elements in `vE` are  between 1 and $E$ (and not 0 and $E-1$);\n",
    "* `vE[1]` represents the largest power and `vE[N]` represents units."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "eca0dd0c",
   "metadata": {},
   "outputs": [],
   "source": [
    "function convertBasisE10(vE::Vector{I}, E::I)::I where {I<:Integer}\n",
    "# converts the a vector vE into an integer p \n",
    "\n",
    "    @assert (minimum(vE)≥1)&&(maximum(vE)≤E)\n",
    "    \n",
    "    N = length(vE)    \n",
    "    p = one(I) #otherwise p lieas between 0 and (E+1)^N - 1\n",
    "    for k ∈ eachindex(vE)\n",
    "        p += (vE[k]-1)*E^(N-k)\n",
    "    end\n",
    "    return p\n",
    "end;"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "888b13e7",
   "metadata": {},
   "source": [
    "#### Example\n",
    "\n",
    "We convert `[2, 2, 1]` (i.e., 110 with a more usual binary notation) into base 10."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "f13bdb44",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "7"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "convertBasisE10([2, 2, 1], 2)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "62339486",
   "metadata": {},
   "source": [
    "### From base 10 to base `E`\n",
    "\n",
    "The function `convertBasis10E(p, E, N)` converts a number expressed in base-10 (and represented as the integer p) into its base-`E` representation `vE` (as a vector of length `N`). This is the inverse of `convertBasisE10`.\n",
    "\n",
    "Same remarks as for `convertBasisE10`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "dfea1fbe",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "convertBasis10E (generic function with 1 method)"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "function  convertBasis10E(p::I, E::I, N::I)::Vector{I} where{I<:Integer}\n",
    "\n",
    "    vE = zeros(I, N)    \n",
    "    @assert (p≥1)&&(p≤E^N)\n",
    "    ptemp = p-1;\n",
    "    for k ∈ eachindex(vE)\n",
    "        ptemp, r = divrem(ptemp,E)\n",
    "        vE[N-k+1] = 1+r        \n",
    "    end\n",
    "    return vE\n",
    "end"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ca8ba731",
   "metadata": {},
   "source": [
    "#### Example\n",
    "\n",
    "We check that 7 converts back into  `[2, 2, 1]`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "2cf4a48b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3-element Vector{Int64}:\n",
       " 2\n",
       " 2\n",
       " 1"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "convertBasis10E(7, 2, 3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "41154ebf",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Julia 1.10.5",
   "language": "julia",
   "name": "julia-1.10"
  },
  "language_info": {
   "file_extension": ".jl",
   "mimetype": "application/julia",
   "name": "julia",
   "version": "1.8.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
